package org.example.service;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import lombok.extern.slf4j.Slf4j;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.example.service.consts.BiliBiliConst;
import org.example.util.BilibiliWbiUtil;
import org.example.dto.bilibili.ServiceData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.HttpCookie;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;

@Slf4j
public class BiliBiliDanmuService {

    private static final Logger logger = LoggerFactory.getLogger(BiliBiliDanmuService.class);
    /**
     * 真实房间号
     */

    private Integer roomId;
    private Integer realRoomId;

    private List<String> serverUrls = new ArrayList<>(16);

    private String token;

    private String buvid3;

    private String buvid4;

    public BiliBiliDanmuService(Integer roomId) {
        this.roomId = roomId;
    }

    /**
     * 获取真实房间id
     */
    public void getRealRoomId() {
        Map<String, Object> params = new HashMap<>();
        params.put("room_id", this.roomId);
        logger.info("roomId={}", roomId);
        String responseStr = HttpUtil.get(BiliBiliConst.GET_LIVEROOM_INFO_URL, params);
        ServiceData<JSONObject> serviceData = JSONObject.parseObject(responseStr, ServiceData.class);
        if (Objects.isNull(serviceData)) {
            logger.error("getRealRoomId: serviceData is null");
            throw new RuntimeException("getRealRoomId: serviceData is null");
        }
        if (serviceData.getCode() != 0) {
            logger.error("get live room info error,roomId={},errCode={},errMsg={}", roomId, serviceData.getCode(),
                    serviceData.getMessage());
            throw new RuntimeException(serviceData.getMessage());
        }
        int realRoomId = serviceData.getData().getInteger("room_id");
        logger.info("真实房间id={}", realRoomId);
        this.realRoomId = realRoomId;
    }

    public String getMinxinKey() {
        String responseStr = HttpUtil.get(BiliBiliConst.NAV_URL);
        ServiceData<JSONObject> serviceData = JSONObject.parseObject(responseStr, ServiceData.class);
        if (Objects.isNull(serviceData)) {
            logger.error("getRealRoomId: serviceData is null");
            throw new RuntimeException("NAVUrl: serviceData is null");
        }
        if (serviceData.getCode() != 0 && serviceData.getCode() != -101) {
            logger.error("get NAV info error,roomId={},errCode={},errMsg={}", roomId, serviceData.getCode(),
                    serviceData.getMessage());
            throw new RuntimeException(serviceData.getMessage());
        }

        JSONObject wbiImgInfo = serviceData.getData().getJSONObject("wbi_img");
        String imgUrl = wbiImgInfo.getString("img_url");
        String subUrl = wbiImgInfo.getString("sub_url");
        String imgKey = getKeyFromPngUrl(imgUrl);
        String subKey = getKeyFromPngUrl(subUrl);
        logger.info("imgKey={},subKey={}", imgKey, subKey);
        if (StrUtil.isEmptyIfStr(imgKey) || StrUtil.isEmptyIfStr(subKey)) {
            logger.error("get wbi key error,imgKey={},subKey={}", imgKey, subKey);
            throw new RuntimeException("get wbi key error");
        }
        return BilibiliWbiUtil.getMixinKey(imgKey, subKey);
    }

    private String getKeyFromPngUrl(String pngUrl) {
        if (StrUtil.isEmptyIfStr(pngUrl)) {
            return "";
        }
        int lastSlashIndex = pngUrl.lastIndexOf('/');
        int dotIndex = pngUrl.lastIndexOf('.');

        if (lastSlashIndex == -1 || dotIndex == -1 || dotIndex <= lastSlashIndex) {
            return "";
        }

        return pngUrl.substring(lastSlashIndex + 1, dotIndex);
    }

    public void getDanmuInfo() {
        TreeMap<String, Object> params = new TreeMap<>();
        params.put("id", realRoomId);
        params.put("type",0);
        params.put("w_ts", System.currentTimeMillis() / 1000);
        String mixinKey = getMinxinKey();
        String wRid = BilibiliWbiUtil.getWbiSign(params, mixinKey);
        params.put("w_rid", wRid);
        // String responseStr = HttpUtil.get(BiliBiliConst.GET_DANMU_INFO_URL, params);
        String responseStr = HttpUtil.createGet(BiliBiliConst.GET_DANMU_INFO_URL)
        .cookie(new HttpCookie("buvid3",this.buvid3),new HttpCookie("buvid4",this.buvid4))
        .form(params).execute().body();
        ServiceData<JSONObject> serviceData = JSONObject.parseObject(responseStr, ServiceData.class);
        if (serviceData.getCode() != 0) {
            logger.error("getDanmuInfo error,errCode={},errMsg={}", serviceData.getCode(), serviceData.getMessage());
            return;
        }
        JSONObject data = serviceData.getData();
        if (Objects.isNull(data)) {
            return;
        }
        JSONArray hostList = data.getJSONArray("host_list");
        hostList.forEach(host -> {
            JSONObject hostObj = (JSONObject) host;
            String hostUrl = hostObj.getString("host");
            Integer port = hostObj.getInteger("wss_port");
            if (StrUtil.isEmpty(hostUrl) || Objects.isNull(port)) {
                return;
            }
            String url = String.format("wss://%s:%d", hostUrl, port);
            this.serverUrls.add(url);
        });
        logger.info("getDanmuInfo: serverUrls={}", serverUrls.toString());
        this.token = data.getString("token");
        logger.info("getDanmuInfo: token={}", token);
    }

    public void getbuvid() {
        String responseStr = HttpUtil.get(BiliBiliConst.GET_BUVID_URL);
        ServiceData<JSONObject> serviceData = JSONObject.parseObject(responseStr, ServiceData.class);
        if (Objects.isNull(serviceData)) {
            logger.error("getbuvid: serviceData is null");
            throw new RuntimeException("getbuvid: serviceData is null");
        }
        if (serviceData.getCode() != 0) {
            logger.error("getbuvid: serviceData code is not 0,msg={}", serviceData.getMessage());
            throw new RuntimeException(serviceData.getMessage());
        }

        this.buvid3 = serviceData.getData().getString("b_3");
        this.buvid4 = serviceData.getData().getString("b_4");
    }

    public void start() {
        try {
            getRealRoomId();
            getbuvid();
            getDanmuInfo();
            URI uri = new URI(this.serverUrls.get(0) + "/sub");
            BiliBiliDanmuClient client = new BiliBiliDanmuClient(uri, this.realRoomId, this.token, this.buvid3);
            client.connect();
        } catch (URISyntaxException ex) {
            logger.error("uri error,", ex);
        }
        while (true) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
                logger.error("Main thread interrupted", ex);
            }
        }
    }

    public static void main(String[] args) {
        BiliBiliDanmuService service = new BiliBiliDanmuService(7777);
        service.start();
    }
}
